#pragma once

#include <utility>
#include <unordered_map>
#include <string>
#include <cstring>
#include "media.h"
#include "AudioBuffer.h"
#include "rtp.h"

namespace jlh
{
	class RtspRequest
	{
	public:
		enum Method
		{
			OPTIONS=0,DESCRIBE,SETUP,PLAY,TEARDOWN,GET_PARAMETER,
			RTCP,NONE
		};

		const char* MethodToString[8]=
		{
			"OPTIONS","DESCRIBE","SETUP","PLAY","TEARDOWN","GET_PARAMETER",
			"RTCP","NONE"
		};

		enum RtspRequestParseState
		{
			kParseRequestLine,
			kParseHeaderLine,
			kGotAll
		};

		bool ParseRequest(AudioBuffer& buffer);

		bool GotAll() const
		{
			return kGotAll == state_;
		}

		void Reset()
		{
			state_ = kParseRequestLine;
			request_line_param_.clear();
			header_line_param_.clear();
		}

		Method GetMethod() const {
			return method_;
		}

		MediaChannelId GetChannelId() const {
			return channel_id_;
		}

		uint8_t GetRtpChannel() const;
		uint8_t GetRtcpChannel() const;
		uint16_t GetRtpPort() const;
		uint16_t GetRtcpPort() const;
		TransportMode GetTransportMode() const {
			return transport_;
		}
		std::string GetRtspUrlSuffix();
		uint32_t GetCSeq() const;
		int BuildOptionRes(char* buf, int buf_size);
		int BuildNotFoundRes(char* buf, int buf_size);
		int BuildServerErrorRes(char* buf, int buf_size);
		int BuildDescribeRes(char* buf, int buf_size, const char* sdp);
		int BuildSetupTcpRes(char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint32_t session_id);
		int BuildSetupUdpRes(char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint32_t session_id);
		int BuildUnsupportedRes(char* buf, int buf_size);
		int BuildPlayRes(char* buf, int buf_size, const char* rtpInfo, uint32_t session_id);
		int BuildTeardownRes(char* buf, int buf_size, uint32_t session_id);
		int BuildGetParameterRes(char* buf, int buf_size, uint32_t session_id);

	private:
		bool ParseRequestLine(const char* begin, const char* end);
		bool ParseHeadersLine(const char* begin, const char* end);

		bool ParseCSeq(std::string& message);
		bool ParseAuthorization(std::string& message);
		bool ParseAccept(std::string& message);
		bool ParseTransport(std::string& message);
		bool ParseMediaChannel(std::string& message);
		bool ParseSessionId(std::string& message);

	private:
		Method method_ = NONE;
		MediaChannelId channel_id_;
		TransportMode transport_;

		std::string auth_response_;
		std::unordered_map<std::string, std::pair<std::string, uint32_t>> request_line_param_;
		std::unordered_map<std::string, std::pair<std::string, uint32_t>> header_line_param_;
		RtspRequestParseState state_ = kParseRequestLine;
	};


	class RtspResponse
	{
	public:
		enum Method
		{
			OPTIONS=0,DESCRIBE,ANNOUNCE,SETUP,RECORD,RTCP,NONE
		};

		bool ParseResponse(AudioBuffer& buffer);

		void SetUserAgent(const char* user_agent)
		{
			user_agent_ = std::string(user_agent);
		}

		void SetRtspUrl(const char* url)
		{
			rtsp_url_ = std::string(url);
		}

		Method GetMethod() const
		{
			return method_;
		}

		uint32_t GetCSeq() const
		{
			return cseq_;
		}

		std::string GetSession() const
		{
			return session_;
		}

		int BuildOptionReq(char* buf, int buf_size);
		int BuildAnnounceReq(char* buf, int buf_size, const char* sdp);
		int BuildSetupTcpReq(char* buf, int buf_size, int trackId);
		int BuildRecordReq(char* buf, int buf_size);

	private:
		Method method_ = NONE;
		uint32_t cseq_ = 0;
		std::string user_agent_;
		std::string rtsp_url_;
		std::string session_;
	};
}
